/**
 * 
 */
package hr.fer.zemris.annotator;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import hr.fer.zemris.annotator.exceptions.PerformanceAnnotatorException;
import hr.fer.zemris.annotator.exceptions.ShellExecutionException;
import hr.fer.zemris.annotator.model.ApplicationParameters;
import hr.fer.zemris.annotator.model.Method;
import hr.fer.zemris.annotator.model.MethodData;
import hr.fer.zemris.annotator.services.CommandBuilderService;
import hr.fer.zemris.annotator.services.ProfilingDataParsingService;
import hr.fer.zemris.annotator.settings.ApplicationSettings;

/**
 * @author dajan
 * 
 */
public class PerformanceAnnotator {

	public static String runPerformanceAnnotator() {

		String returnStatus = "";
		MethodData methodData = null;
		File[] sourceFiles;
		// main workflow
		try {
			returnStatus = runProfileScript();
			methodData = ProfilingDataParsingService
					.parseGprofileOutput(CommandBuilderService
							.getProfilingOutputPath());

			sourceFiles = getSourceFiles();

			for (int i = 0; i < sourceFiles.length; i++) {

				annotateSourceFile(sourceFiles[i], methodData);

			}

		} catch (PerformanceAnnotatorException e) {

			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// profilinig file not found
			e.printStackTrace();
		} catch (IOException e) {
			// IO exception while parsing profiniling data file
			e.printStackTrace();
		}

		return returnStatus;
	}

	private static void annotateSourceFile(File sourceFile,
			MethodData methodData) throws IOException {

		String fileString = CommandBuilderService.getStringFromFile(sourceFile);
		List<String> methodDefinitions = new ArrayList<String>();
		BufferedWriter writer = new BufferedWriter(new FileWriter(
				CommandBuilderService.getOutputFilePath(sourceFile)));

		Pattern pattern = Pattern
				.compile(ApplicationSettings.C_FUNCTION_DEFINITION_REGEX);

		Matcher matcher = pattern.matcher(fileString);

		while (matcher.find()) {

			methodDefinitions.add(matcher.group());

		}

		for (String method : methodDefinitions) {
			pattern = Pattern.compile(method, Pattern.LITERAL);
			matcher = pattern.matcher(fileString);
			fileString = matcher.replaceAll(CommandBuilderService
					.getAnnotationString(method, methodData));
		}

		fileString = CommandBuilderService.getMemoString()
				+ CommandBuilderService
						.getProfilingDataExplanation(ApplicationSettings.FLAT_PROFILING_DATA)
				+ CommandBuilderService
						.getProfilingDataExplanation(ApplicationSettings.CALL_GRAPH_DATA)
				+ fileString;

		writer.write(fileString);
		writer.flush();
		writer.close();

		// fileString = matcher.replaceAll("daaaajaaaan");

		// System.out.println(fileString);
		// }
	}

	private static File[] getSourceFiles() {

		File sourceDir = new File(ApplicationParameters.getInstance()
				.getSourcePath() + "/");
		File[] sourceFiles = sourceDir.listFiles();

		return sourceFiles;

	}

	private static String runProfileScript()
			throws PerformanceAnnotatorException {

		// . profile_c_code.sh /home/dajan/development/dipl/c_examples/
		// sort_example.o /home/dajan/sort_example2.txt

		// String command = CommandBuilderService.getProfileCommand();

		String command = CommandBuilderService.getProfileCommand();
		ProcessBuilder pb = new ProcessBuilder(
				ApplicationSettings.APPLICATION_DEFAULT_TERMINAL, "-c", command);
		String status = "";

		Process shell;
		try {
			shell = pb.start();
			shell.waitFor();
			InputStream shellIn = shell.getInputStream();
			BufferedReader shellReader = new BufferedReader(
					new InputStreamReader(shellIn));

			while (true) {
				String line = shellReader.readLine();
				if (line == null)
					break;
				status += line;
				status += System.getProperty("line.separator");
			}
			// close the stream
			try {
				shellIn.close();
			} catch (IOException ignoreMe) {
			}

		} catch (IOException e) {

			e.printStackTrace();
			throw new ShellExecutionException(e.getMessage());

		} catch (InterruptedException e) {

			e.printStackTrace();
			throw new ShellExecutionException(e.getMessage());
		}

		return status;

	}

}
